"java"

STATIC_MTREE = """\
etc/ssl/certs/ time=946684800.0 mode=755 gid=0 uid=0 type=dir
etc/ssl/certs/java/ time=946684800.0 mode=755 gid=0 uid=0 type=dir
usr/lib/jvm/ time=946684800.0 mode=755 gid=0 uid=0 type=dir
# NOTE: cacerts is moved to ./etc/ssl/certs/java/cacerts via the awk mutation hence 
# a symlink created in the original location for completeness.
usr/lib/jvm/%s/lib/security/cacerts nlink=0 time=946684800.0 mode=777 gid=0 uid=0 type=link link=/etc/ssl/certs/java/cacerts
"""

AWK = """\
{
    sub("^"  "output/lib/security/cacerts", "./etc/ssl/certs/java/cacerts")
    sub("^"  "output", "./usr/lib/jvm/%s")
    sub(/time=[0-9\\.]+/, "time=946684800.0");
    if ($1 ~ ".*legal/.*" || $1 ~ ".*conf/.*") {    
        # keep it as 0755
        # or 0644 if its a file
        if ($0 ~ ".*type=file.*") {  
            sub("mode=0755", "mode=0644")
        } 
    } else if ($1 ~ ".*\\.jsa") {    
        sub("mode=0755", "mode=0644")
    } if ($0 ~ ".*type=dir.*") {  
        # keep the 0755 permission override
    } else {
        sub("mode=0755", "")
    }
    # pkg_tar strips the leading ./ so we do too to avoid 
    # `duplicates of file paths not supported` error
    sub("^"  "./", "")
    print
}
"""

BUILD_TMPL = """\
# GENERATED BY temurin_archive.bzl
load("@distroless//private/pkg:debian_spdx.bzl", "debian_spdx")
load("@distroless//private/util:merge_providers.bzl", "merge_providers")
load("@aspect_bazel_lib//lib:tar.bzl", "tar", "mtree_spec")
load("@rules_pkg//:pkg.bzl", "pkg_tar")

SRCS = glob(["output/**/*"])
mtree_spec(
    name = "mtree",
    srcs = SRCS,
)

genrule(
    name = "mutate_mtree",
    srcs = [":mtree"],
    tools = ["static.mtree", "mutate.awk"],
    outs = ["out.mtree"],
    cmd = "cat $(execpath :static.mtree) >$@ && awk -f $(execpath :mutate.awk) <$< >>$@ && sort -o $@ $@"
)

tar(
    name = "data",
    srcs = SRCS,
    mtree = "out.mtree"
)

pkg_tar(
    name = "_control",
    srcs = ["control"],
)

debian_spdx(
    name = "spdx",
    control = ":_control.tar",
    data = ":data.tar",
    package_name = "{package_name}",
    spdx_id = "{spdx_id}",
    sha256 = "{sha256}",
    urls = [{urls}]
)

merge_providers(
    name = "{name}",
    srcs = [":data", ":spdx"],
    visibility = ["//visibility:public"],
)
"""

def _impl(rctx):
    name = rctx.attr.name.split("~")[-1]
    rctx.report_progress("Fetching {}".format(rctx.attr.package_name))
    rctx.download_and_extract(
        url = rctx.attr.urls,
        sha256 = rctx.attr.sha256,
        stripPrefix = rctx.attr.strip_prefix,
        output = "output",
    )
    rctx.file("static.mtree", STATIC_MTREE % name)
    rctx.file("mutate.awk", AWK % name)
    rctx.template(
        "control",
        rctx.attr.control,
        substitutions = {
            "{{VERSION}}": rctx.attr.version,
            "{{ARCHITECTURE}}": rctx.attr.architecture,
            "{{SHA256}}": rctx.attr.sha256,
        },
    )
    rctx.file(
        "BUILD.bazel",
        content = BUILD_TMPL.format(
            name = name,
            package_name = rctx.attr.package_name,
            version = rctx.attr.version,
            spdx_id = rctx.attr.name,
            urls = ",".join(['"%s"' % url for url in rctx.attr.urls]),
            sha256 = rctx.attr.sha256,
        ),
    )

temurin_archive = repository_rule(
    implementation = _impl,
    attrs = {
        "urls": attr.string_list(mandatory = True),
        "sha256": attr.string(mandatory = True),
        "strip_prefix": attr.string(),
        "package_name": attr.string(default = "temurin"),
        "version": attr.string(mandatory = True),
        "plain_version": attr.string(mandatory = True),
        "architecture": attr.string(mandatory = True),
        # control is only used to populate the sbom, see https://github.com/GoogleContainerTools/distroless/issues/1373
        # for why writing debian control files to the image is incompatible with scanners.
        "control": attr.label(),
    },
)

def _version_repo_impl(rctx):
    rctx.file(
        "versions.bzl",
        content = "JAVA_RELEASE_VERSIONS={}".format(rctx.attr.versions),
    )
    rctx.file("BUILD.bazel", 'exports_files(["versions.bzl"])')

version_repo = repository_rule(
    implementation = _version_repo_impl,
    attrs = {
        "versions": attr.string_dict(),
    },
)

def _java_impl(module_ctx):
    mod = module_ctx.modules[0]

    if len(module_ctx.modules) > 1:
        fail("java.archive should be called only once")
    if not mod.is_root:
        fail("java.archive should be called from root module only.")

    direct_deps = ["java_versions"]
    versions = {}

    for mod in module_ctx.modules:
        for archive in mod.tags.archive:
            direct_deps.append(archive.name)
            versions[archive.name] = archive.plain_version
            temurin_archive(
                name = archive.name,
                urls = archive.urls,
                sha256 = archive.sha256,
                strip_prefix = archive.strip_prefix,
                package_name = archive.package_name,
                version = archive.version,
                plain_version = archive.plain_version,
                architecture = archive.architecture,
                control = "//java:control",
            )

    version_repo(
        name = "java_versions",
        versions = versions,
    )

    return module_ctx.extension_metadata(
        root_module_direct_deps = direct_deps,
        root_module_direct_dev_deps = [],
    )

_archive = tag_class(attrs = {
    "name": attr.string(mandatory = True),
    "urls": attr.string_list(mandatory = True),
    "sha256": attr.string(mandatory = True),
    "strip_prefix": attr.string(),
    "package_name": attr.string(default = "temurin"),
    "version": attr.string(mandatory = True),
    "plain_version": attr.string(mandatory = True),
    "architecture": attr.string(mandatory = True),
})

java = module_extension(
    implementation = _java_impl,
    tag_classes = {
        "archive": _archive,
    },
)
